package logic;

import java.util.ArrayList;
import java.util.Stack;

import ast.*;

/** 
 * Crea el arbol semantico 
 */

public class SemanticAnalizer {

	/**
     * ATTRIBUTES
     */
	private ArrayList<Nodo> _PilaNodosPadres = new ArrayList<Nodo>();
	private Program 	_Program	  = new Program();
	private int			_AnteriorPosX = 0;
	private int			_AnteriorPosY = 0;
	private String 		_ErrorLog     = "";
	private boolean		_RemovePadre  = false; 	
	
	
	public void analizarPrograma( Board pMyProgram ){
		ArrayList<Pestanna> Pestannas = pMyProgram.get_Pestannas();
		// recorrer las pestannas del programa
		for( int cnt=0; cnt<Pestannas.size(); cnt++ ){
			Pestanna pestannaActual =  Pestannas.get(cnt);
			Function funcionActual = new Function( pestannaActual.get_Id() );
			_Program.addFunction( funcionActual );
			// add Funcion a la pila
			_PilaNodosPadres.add(funcionActual);
			// analizar pestanna
			analizarPestanna( pestannaActual, 0, 0 );
		}
	}
	
	private void analizarPestanna( Pestanna pPestanna, int pPosX, int pPosY ){
		Block blockActual = pPestanna.getBlock(pPosX, pPosY);
		Expresion exp =  null;
		int ultimoPadre = 0;
		// es un pipe y es horizontal?
		if( blockActual.getType() == Constantes.TYPE_PIPE && ((Pipe)blockActual).get_Id() == Constantes.ID_WE ){
			// es una asignacion a flujo?
			if( pPosY>0 && (pPestanna.getBlock(pPosX, pPosY-1))!= null && 
			   (pPestanna.getBlock(pPosX, pPosY-1)).getType() == Constantes.TYPE_VAR ){
				exp = new ExpAsigFlujo( ((Var)pPestanna.getBlock(pPosX, pPosY-1)).getId() );			
			// es una asignacion a var?
			} else if( pPosY<Constantes.CASILLAS_Y && (pPestanna.getBlock(pPosX, pPosY+1))!= null && 
					  (pPestanna.getBlock(pPosX, pPosY+1)).getType() == Constantes.TYPE_VAR ){
				exp = new ExpAsigVar( ((Var)pPestanna.getBlock(pPosX, pPosY+1)).getId() );	
			}
		// es un command?
		} else if( blockActual.getType() == Constantes.TYPE_COMMAND  ){
			switch( ((Command)blockActual).get_Id() ) {
				case Constantes.ID_DELAY:
					exp = new ExpAsigVar( ((Var)pPestanna.getBlock(pPosX, pPosY-1)).getId() );	
					break;
				case Constantes.ID_LED:
					int idVarTiempo		= -1;
					boolean modificador	= false; 
					if( (pPestanna.getBlock(pPosX, pPosY-1))!=null && pPosY>0 &&
						((Command)(pPestanna.getBlock(pPosX, pPosY-1))).get_Id() == Constantes.ID_MODIF ){
						modificador = true;
					}
					if( (pPestanna.getBlock(pPosX+1, pPosY))!=null &&  (pPestanna.getBlock(pPosX+1, pPosY)).getType() == Constantes.TYPE_COMMAND &&
						((Command)(pPestanna.getBlock(pPosX+1, pPosY))).get_Id() == Constantes.ID_DELAY ){
							idVarTiempo = ((Var)pPestanna.getBlock(pPosX+1, pPosY-1)).getId();
					}	
					exp = new ExpLed( ((Command)blockActual).getPin(), idVarTiempo, modificador, ((Var)pPestanna.getBlock(pPosX, pPosY+1)).getId() );		
					break;
				case Constantes.ID_7SEG:
					exp = new Exp7Seg( ((Command)blockActual).getPin(), ((Var)pPestanna.getBlock(pPosX+1, pPosY-1)).getId() );	
					break;
				case Constantes.ID_POTENC:
					if( (pPestanna.getBlock(pPosX, pPosY-1))!=null && (pPestanna.getBlock(pPosX, pPosY-1)).getType() == Constantes.TYPE_VAR ){
						idVarTiempo = ((Var)pPestanna.getBlock(pPosX, pPosY-1)).getId();
					}else{
						idVarTiempo = 0;
					}
					exp = new ExpEntrada( ((Command)blockActual).getPin(), idVarTiempo, ((Var)pPestanna.getBlock(pPosX, pPosY+1)).getId(), false );		
					break;
				case Constantes.ID_BOTON:
					if( (pPestanna.getBlock(pPosX, pPosY-1))!=null && (pPestanna.getBlock(pPosX, pPosY-1)).getType() == Constantes.TYPE_VAR ){
						idVarTiempo = ((Var)pPestanna.getBlock(pPosX, pPosY-1)).getId();
					}else{
						idVarTiempo = 0;
					}
					exp = new ExpEntrada( ((Command)blockActual).getPin(), idVarTiempo, ((Var)pPestanna.getBlock(pPosX, pPosY+1)).getId(), true );		
					break;
				case Constantes.ID_FOTOREST:
					if( (pPestanna.getBlock(pPosX, pPosY-1))!=null && (pPestanna.getBlock(pPosX, pPosY-1)).getType() == Constantes.TYPE_VAR ){
						idVarTiempo = ((Var)pPestanna.getBlock(pPosX, pPosY-1)).getId();
					}else{
						idVarTiempo = 0;
					}
					exp = new ExpEntrada( ((Command)blockActual).getPin(), idVarTiempo, ((Var)pPestanna.getBlock(pPosX, pPosY+1)).getId(), false );		
					break;					
				case Constantes.ID_IF:
					int posYVar = ultimaVarIf( pPestanna, pPosX, pPosY );
					exp = new ExpIf();
					while( (pPestanna.getBlock(pPosX, posYVar))!=null &&  (pPestanna.getBlock(pPosX, posYVar)).getType() == Constantes.TYPE_VAR ){
						IfCase ifCase = new IfCase( ((Var)pPestanna.getBlock(pPosX, posYVar)).getId() );
						// agregar caso al fin
						((ExpIf)exp).addIfCase(ifCase);
						// agregar caso a la pila de padres
						_PilaNodosPadres.add(ifCase);
						agregarAlArbol( pPestanna, pPosX+1, posYVar, exp );
						// sig caso
						posYVar--;
					}
					// contenido del else
					IfCase expElse = new IfCase(0);
					((ExpIf)exp).setElse(expElse);
					// agregar caso a la pila de padres
					_PilaNodosPadres.add(expElse);
					agregarAlArbol( pPestanna, pPosX+1, posYVar-1, exp );
					break;
				case Constantes.ID_END:
					// hacer pop al padre
					ultimoPadre = _PilaNodosPadres.size()-1;
					System.out.println( ultimoPadre );
					_RemovePadre = true;
					exp = new ExpFinal();
					break;
				case Constantes.ID_FUNTION:
					/// hacer pop al padre
					ultimoPadre = _PilaNodosPadres.size()-1;
					_RemovePadre = true;
					exp = new ExpLlamarFunction( ((Call)blockActual).get_FuncionID() );
					break;
				case Constantes.ID_INICIO:
					exp = new ExpInicio( pPestanna.get_Id(), pPestanna.get_Variables() );
					break;
			};
		}
		agregarAlArbol( pPestanna, pPosX, pPosY, exp );
	}
	
	private void agregarAlArbol( Pestanna pPestanna, int pPosX, int pPosY, Expresion pExp ){
		// if se cre� una expresion? agregalo al arbol
		int ultimoPadre = _PilaNodosPadres.size()-1;
		if( pExp != null &&  !_PilaNodosPadres.isEmpty() ){
			(_PilaNodosPadres.get(ultimoPadre)).addExpresion( pExp );
			if( _RemovePadre ){
				_PilaNodosPadres.remove(ultimoPadre);
				_RemovePadre = false;
			}
		}
		
		// get posicion de siguiente block
		int coord[] = pPestanna.getPosBlockSig( pPosX, pPosY, _AnteriorPosX, _AnteriorPosY);
		_AnteriorPosX = pPosX;
		_AnteriorPosY = pPosY;
		// si no encontr� el sig block
		if( coord == null ){
			// si la exp actual es un if: debe cerrarse el nodo padre.
			// TODO: corregir alambrado
			if( pExp.getClass().getName() == "ast.ExpIf"){
				Expresion exp = new ExpFinal();
				(_PilaNodosPadres.get(ultimoPadre)).addExpresion( exp );
			// si por error no se coloc� un final a la expresion.
			}else if( pExp.getClass().getName() != "ast.ExpFinal" ){
				_ErrorLog += "ERROR: Es necesario colocar un final a la expresi�n en la pesta�a \""+ pPestanna.get_Nombre() + "\". \n";
			}
		} else {
			// llamada recursiva
			analizarPestanna( pPestanna, coord[Constantes.POS_X], coord[Constantes.POS_Y] );
		}
	}
	
	private int ultimaVarIf( Pestanna pPestanna, int pPosX, int pPosY ){
		int PosYDeVar = pPosY+1;
		while( pPosY<Constantes.CASILLAS_Y && (pPestanna.getBlock(pPosX, pPosY+1)).getType() == Constantes.TYPE_VAR ){
			PosYDeVar++;
		}
		return PosYDeVar;
	}

	public String getErrorLog() {
		return _ErrorLog;
	}
	
	public Program getArbolSemantic() {
		return _Program;
	}
	
}
